Passed
Push — master ( f91fcb...083070 )
by Night
01:21
created

stringFuncs.indexOfMany   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 3
nop 2
dl 0
loc 14
rs 9.3333
c 0
b 0
f 0
1
/** global: UB */
2
3
var stringFuncs = {
4
	
5
	
6
	// primary search functions
7
	indexOf: function(search, startAt = null){
8
		return this.smartIndexOf(search, true, true, startAt);
9
	},
10
	indexOfCI: function(search, startAt = null){
11
		return this.smartIndexOf(search, false, true, startAt);
12
	},
13
	smartIndexOf: function(search, caseSensitive = true, first = true, startAt = null, substringIsLower = false, wholeWords = false){
14
		var text = this;
15
		
16
		// temps
17
		var sl = search.length;
18
		var ml = (text.length - sl);
19
		
20
		// quick checks
21
		if (ml < sl) {
22
			return -1;
23
		}
24
		
25
		// only check equality of both strings of equal length
26
		if (ml == sl) {
27
			if (caseSensitive && !wholeWords) {
28
				return (text == search) ? 0 : -1;
29
			}
30
			return text.isEqual(search, caseSensitive, false, substringIsLower) ? 0 : -1;
31
		}
32
		
33
		
34
		// default start at
35
		if (first) {
36
			if (startAt == null) {
37
				startAt = 0;
38
			}
39
		} else {
40
			if (startAt == null) {
41
				startAt = ml - sl;
42
			}
43
		}
44
		
45
		
46
		// if using whole words, slower version is used
47
		if (wholeWords) {
48
			var regex = UB.regex.New(search, wholeWords, caseSensitive, true);
49
			if (first) {
50
				var i = startAt === 0 ? text.search(regex) : text.substring(startAt).search(regex);
51
			} else {
52
				/*var i:int = startAt == (ml - sl) ? text.search(regex) : text.substring(startAt).search(regex);*/
53
				/// unsupported
54
				i = -1;
55
			}
56
			return i;
57
		}
58
		
59
		
60
		
61
		if (first) {
62
			
63
			// FIRST INDEX
64
			
65
			// CASE INSENSITIVE
66
			if (!caseSensitive) {
67
				
68
				// very fast CI comparison
69
				return text._indexOfCI(search, startAt, substringIsLower, sl, ml);
70
				
71
				// much faster than this:
72
				//return text.toUpperCase().indexOf(search.toUpperCase(), startAt);
73
			}
74
			
75
			// CASE SENSITIVE
76
			return text.indexOf(search, startAt);
77
			
78
		}
79
		
80
		
81
		// LAST INDEX
82
		
83
		// CASE INSENSITIVE
84
		if (!caseSensitive) {
85
			
86
			// very fast CI comparison
87
			return text._lastIndexOfCI(search, startAt, substringIsLower, sl);
88
			
89
			// much faster than this:
90
			//return text.toUpperCase().lastIndexOf(search.toUpperCase(), startAt);
91
		}
92
		
93
		// CASE SENSITIVE
94
		return text.lastIndexOf(search, startAt);
95
	},
96
97 View Code Duplication
	_indexOfCI: function(search, startAt, substringIsLower, sl, ml){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
98
		var text = this;
99
100
		// init casing tables
101
		if (UB.UTF_lowerToUpper == null){
102
			UB.initCasing();
103
		}
104
		
105
		// per main char
106
		for (var m = startAt;m <= ml;m++){
107
							
108
							
109
			// per substring char
110
			var match = true;
111
			for (var s = 0;s<sl;s++){
112
				
113
				var c1 = text.charCodeAt(m + s);
114
				var c2 = search.charCodeAt(s);
115
				
116
				// CI
117
				if (c1 <= UB.UTF_casingTablesMax){ /// CI
118
					c1 = UB.UTF_upperToLower[c1];
119
				}
120
				if (!substringIsLower){
121
					if (c2 <= UB.UTF_casingTablesMax){ /// CI
122
						c2 = UB.UTF_upperToLower[c2];
123
					}
124
				}
125
				
126
				if (c1 != c2) {
127
					match = false;
128
					break;
129
				}
130
				
131
			}
132
			
133
			if (match){
134
				return m;
135
			}
136
			
137
		}
138
139
		return -1;
140
	},
141
142 View Code Duplication
	_lastIndexOfCI: function(search, startAt, substringIsLower, sl){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
143
		var text = this;
144
		
145
		// init casing tables
146
		if (UB.UTF_lowerToUpper == null){
147
			UB.initCasing();
148
		}
149
		
150
		// per main char
151
		for (var m = startAt;m >= 0;m--){
152
			
153
			
154
			// per substring char
155
			match = true;
0 ignored issues
show
Bug introduced by
The variable match seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.match.
Loading history...
156
			for (var s = 0;s<sl;s++){
157
				
158
				c1 = text.charCodeAt(m + s);
0 ignored issues
show
Bug introduced by
The variable c1 seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.c1.
Loading history...
159
				c2 = search.charCodeAt(s);
0 ignored issues
show
Bug introduced by
The variable c2 seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.c2.
Loading history...
160
				
161
				// CI
162
				if (c1 <= UB.UTF_casingTablesMax){ /// CI
163
					c1 = UB.UTF_upperToLower[c1];
164
				}
165
				if (!substringIsLower){
166
					if (c2 <= UB.UTF_casingTablesMax){ /// CI
167
						c2 = UB.UTF_upperToLower[c2];
168
					}
169
				}
170
				
171
				if (c1 != c2) {
172
					match = false;
173
					break;
174
				}
175
				
176
			}
177
			
178
			if (match){
179
				return m;
180
			}
181
			
182
		}
183
		
184
		return -1;
185
	},
186
187
	countOf: function(find, caseSensitive = true){
188
		var text = this;
189
		
190
		// use regex method for case insensitive comparison
191
		if (!caseSensitive){
192
			var char = UB.regex.Escape(find);
193
			var flags = 'ig';
194
			return parseInt(text.match(new RegExp(char, flags)).length);
195
		}
196
		
197
		// use faster method for case sensitive comparison
198
		var count = 0;
199
		var index = 0;
200
		var len = find.length;
201
		while ((index = text.indexOf(find, index)) > -1) {
202
			count++;
203
			index += len;
204
		}
205
		return count;
206
	},
207
	
208
	/** searches for all the given terms, collects their char indexes (S.IndexOf), and returns the smallest/largest index (depending on `first`) */
209
	indexOfAny: function(searchFor, first = true, caseSensitive = true, startAt = null){
210
		var str = this;
211
212
		// index of first
213
		if (first) {
214
			return str.indexOfFirstAny(searchFor, caseSensitive, startAt);
215
		}
216
217
		// index of last
218
		if (startAt == null) {
219
			startAt = 0;
220
		}
221
		return str.indexOfLastAny(searchFor, caseSensitive, startAt);
222
	},
223
	/** searches for all the given terms, collects their char indexes (S.IndexOf), and returns the largest index */
224
	indexOfLastAny: function(searchFor, caseSensitive = true, startAt = null){
225
		var str = this;
226
		
227
		// case insensitive if wanted
228
		if (!caseSensitive){
229
			str = str.toLowerCase();
230
		}
231
		
232
		// per search term
233
		var indices = [];
234
		for (var s = 0, sl = searchFor.length;s<sl;s++){
235
			var sWord = searchFor[s];
236
			
237
			// case insensitive if wanted
238
			if (!caseSensitive){
239
				sWord = sWord.toLowerCase();
240
			}
241
			
242
			// check where found
243
			indices[s] = str.lastIndexOf(sWord, startAt);
244
		}
245
		
246
		// return last found term
247
		return indices.max();
248
	},
249
	/** searches for all the given terms, collects their char indexes (S.IndexOf), and returns the smallest index */
250
	indexOfFirstAny: function(searchFor, caseSensitive = true, startAt = 0){
251
		var str = this;
252
		
253
		// case insensitive if wanted
254
		if (!caseSensitive){
255
			str = str.toLowerCase();
256
		}
257
		
258
		// per search term
259
		var indices = [];
260
		for (var s = 0, sl = searchFor.length;s<sl;s++){
261
			var sWord = searchFor[s];
262
			
263
			// case insensitive if wanted
264
			if (!caseSensitive){
265
				sWord = sWord.toLowerCase();
266
			}
267
			
268
			// check where found
269
			indices[s] = str.indexOf(sWord, startAt);
270
			
271
			// change -1 otherwise looks like first found term
272
			if (indices[s] === -1) {
273
				indices[s] = UB.intMaxValue;
274
			}
275
		}
276
		
277
		// return first found term
278
		return indices.min();
279
	},
280
	endIndexOfLastAny: function(searchFor, caseSensitive = true){
281
		var str = this;
282
		
283
		// case insensitive if wanted
284
		if (!caseSensitive){
285
			str = str.toLowerCase();
286
		}
287
		
288
		// per search term
289
		var indices = [];
290
		for (var s = 0, sl = searchFor.length;s<sl;s++){
291
			var sWord = searchFor[s];
292
			
293
			// case insensitive if wanted
294
			if (!caseSensitive){
295
				sWord = sWord.toLowerCase();
296
			}
297
			
298
			// check where found
299
			indices[s] = str.endIndexOfLast(sWord);
300
		}
301
		
302
		// return last found term
303
		return indices.max();
304
	},
305
	endIndexOfFirstAny: function(searchFor, caseSensitive = true){
306
		var str = this;
307
		
308
		// case insensitive if wanted
309
		if (!caseSensitive){
310
			str = str.toLowerCase();
311
		}
312
		
313
		// per search term
314
		var indices = [];
315
		for (var s = 0, sl = searchFor.length;s<sl;s++){
316
			var sWord = searchFor[s];
317
			
318
			// case insensitive if wanted
319
			if (!caseSensitive){
320
				sWord = sWord.toLowerCase();
321
			}
322
			
323
			// check where found
324
			indices[s] = str.endIndexOfFirst(sWord);
325
			
326
			// change -1 otherwise looks like first found term
327
			if (indices[s] === -1) {
328
				indices[s] = UB.intMaxValue;
329
			}
330
		}
331
		
332
		// return first found term
333
		return indices.min();
334
	},
335
	endIndexOfFirst: function(find){
336
		var str = this;
337
		var pos = str.indexOf(find);
338
		if (pos === -1) return -1;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
339
		return pos + find.length;
340
	},
341
	endIndexOfLast: function(find){
342
		var str = this;
343
		var pos = str.lastIndexOf(find);
344
		if (pos === -1) return -1;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
345
		return pos + find.length;
346
	},
347
	indexOfNumber: function(startAt = 0, not = false, lenIfNotFound = false){
348
		var str = this;
349
		for (var c = (startAt>0?startAt:0), cl = str.length;c<cl;c++){
350
			var char = str.charAt(c);
351
			if (char.isNumber() != not) {
352
				return c;
353
			}
354
		}
355
		return lenIfNotFound ? str.length : -1;
356
	},
357
	indexOfAlphaNumeric: function(startAt = 0, not = false, lenIfNotFound = false){
358
		var str = this;
359
		for (var c = (startAt>0?startAt:0), cl = str.length;c<cl;c++){
360
			var char = str.charAt(c);
361
			if (char.isAlphaNumeric() != not) {
362
				return c;
363
			}
364
		}
365
		return lenIfNotFound ? str.length : -1;
366
	},
367
	
368
	indexOfMany: function(find, startAt = 0){
369
		var str = this;
370
		
371
		// returns the index, of the FIRST FOUND item in the string
372
		
373
		var found = -1;
374
		for (var f = 0, fl = find.length;f<fl;f++){
375
			var at = str.indexOf(find[f], startAt);
376
			if (at != -1 && (at < found || found === -1)) {
377
				found = at;
378
			}
379
		}
380
		return found;
381
	},
382
	
383
	
384
	indexOfAll: function(find, caseSensitive = true, startAt = 0, wholeWords = false){
385
		var str = this;
386
		var indices = [];
387
		
388
		var len = str.length;
389
		var c = startAt;
390
		while (c < len) {
391
			c = str.smartIndexOf(find, caseSensitive, true, c, false, wholeWords);
392
			if (c === -1) {
393
				break;
394
			}
395
			indices.push(c);
396
			c += find.length;
397
		}
398
		
399
		return indices;
400
	},
401
402
	findAll: function(find){
403
		var str = this.toString();
404
		var matches = [];
405
406
		// if regex return all matches
407
		if (find.isRegex()){
408
			do {
409
				var match = find.exec(str);
410
				if (match) {
411
					matches.push(match);
412
				}
413
			} while (match);
414
		}
415
416
		// if string find and return all matches
417
		if (find.isString()){
418
			var indices = str.indexOfAll();
419
			for (var i=0; i<indices.length; i++){
420
				var index = indices[i];
421
				var match = {startIndex:index, endIndex:index+find.length, text:find};
0 ignored issues
show
Unused Code introduced by
The assignment to variable match seems to be never used. Consider removing it.
Loading history...
Comprehensibility Naming Best Practice introduced by
The variable match already seems to be declared on line 409. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
422
			}
423
		}
424
425
		return matches;
426
	},
427
428
	indexOfNth(search, nth, wholeWords = false, matchCase = true) {
429
		var text = this.toString();
430
431
		// quickly test if text contains wanted var
432
		if (!matchCase || text.contains(search)) {
433
434
			// create regex to find
435
			var regex = UB.regex.New(search, wholeWords, matchCase);
436
			
437
438
			// find and return index if found
439
			var match = null;
440
			for (var n = 0; n < nth; n++) {
441
				match = regex.exec(text);
442
				if (match == null || match < 0) {
443
					return -1;
444
				} else {
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
445
					//startAt = match + 1;
446
				}
447
			}
448
			return match == null ? -1 : match;
449
		}
450
451
		return -1;
452
	},
453
454
	
455
	none:null
456
};
457
458
// register funcs
459
UB.registerFuncs(String.prototype, stringFuncs);